/**
 * Copyright (c) 2015-2017, Henry Yang 杨勇 (gismail@foxmail.com).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */package com.lambkit.db.sql;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.lambkit.db.Sql;
import com.lambkit.util.SqlKit;
import com.lambkit.core.LambkitResult;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class Example implements IExample, Serializable {
	private String tableName;
	private String alias;
	//select
	private String loadColumns = "*";
	//where/filter
	private List<ColumnsGroup> oredColumns;
	//排序
	private String orderBy;
	//暂未实现，等待完善
	private List<JoinOn> joinOns;
	//group查询
	private GroupBy groupBy;
	//查询页码
	private int page = 0;
	//查询个数限制
	private int count = 15;
	
	//数据库配置名称
	private String configName;

	private List<String> warns = new ArrayList<>();

	public Example() {
		oredColumns = CollUtil.newArrayList();
	}
	
	public static Example create(String table) {
		Example example = new Example();
		example.setTableName(table);
		return example;
	}
	
	public static Example create(String table, Columns columns) {
		Example example = new Example();
		example.setTableName(table).addColumns(columns);
		return example;
	}
	
	/**
	 * 第一个ColumnsGroup
	 * @return
	 */
	public ColumnsGroup columns() {
		if(oredColumns.size() > 0) {
			return oredColumns.get(0);
		}
        return createColumns();
    }
	
	/**
	 * 加入or查询ColumnsGroup
	 * @param criteria
	 * @return
	 */
	public Example or(ColumnsGroup criteria) {
        criteria.withOr();
        oredColumns.add(criteria);
        return this;
    }

	/**
	 * 加入and查询ColumnsGroup
	 * @param criteria
	 * @return
	 */
    public Example and(ColumnsGroup criteria) {
        oredColumns.add(criteria);
        return this;
    }
    
    /**
     * 加入查询ColumnsGroup
     * @return
     */
    public ColumnsGroup createColumns() {
    	ColumnsGroup criteria = createColumnsInternal();
        oredColumns.add(criteria);
        return criteria;
    }

    protected ColumnsGroup createColumnsInternal() {
    	ColumnsGroup criteria = new ColumnsGroup();
        return criteria;
    }
    
    public Example join(String mainField, String joinTableName, String joinField, SqlJoinMode type, Columns cols) {
		if(joinOns==null) {
			joinOns = CollUtil.newArrayList();
		}
    	JoinOn joinOn = new JoinOn(tableName, mainField, joinTableName, joinField, type, cols);
		joinOn.setMainAlias(getAlias());
    	joinOns.add(joinOn);
    	return this;
	}
    
    public Example join(String mainField, String joinTableName, String joinField, SqlJoinMode type) {
		if(joinOns==null) {
			joinOns = CollUtil.newArrayList();
		}
    	JoinOn joinOn = new JoinOn(tableName, mainField, joinTableName, joinField, type);
		joinOn.setMainAlias(getAlias());
    	joinOns.add(joinOn);
    	return this;
	}
    
    public Example join(String mainField, String joinTableName, String joinField, Columns cols) {
		if(joinOns==null) {
			joinOns = CollUtil.newArrayList();
		}
    	JoinOn joinOn = new JoinOn(tableName, mainField, joinTableName, joinField, cols);
		joinOn.setMainAlias(getAlias());
    	joinOns.add(joinOn);
    	return this;
	}
    
    public Example join(String mainField, String joinTableName, String joinField) {
		if(joinOns==null) {
			joinOns = CollUtil.newArrayList();
		}
    	JoinOn joinOn = new JoinOn(tableName, mainField, joinTableName, joinField);
		joinOn.setMainAlias(getAlias());
    	joinOns.add(joinOn);
    	return this;
	}
	
	public JoinOn createJoinOn(String mainField, String joinTableName, String joinField, SqlJoinMode type) {
		JoinOn joinOn = new JoinOn(tableName, mainField, joinTableName, joinField, type);
		joinOn.setMainAlias(getAlias());
		if(joinOns==null) {
			joinOns = CollUtil.newArrayList();
		}
    	joinOns.add(joinOn);
		return joinOn;
	}
	
	public JoinOn createJoinOn(String mainField, String joinTableName, String joinField) {
		JoinOn joinOn = new JoinOn(tableName, mainField, joinTableName, joinField);
		joinOn.setMainAlias(getAlias());
		if(joinOns==null) {
			joinOns = CollUtil.newArrayList();
		}
    	joinOns.add(joinOn);
		return joinOn;
	}

    public void add(ColumnsGroup columns) {
    	this.oredColumns.add(columns);
    }
    
    public void clear() {
        oredColumns.clear();
        orderBy = null;
    }
    
	public List<ColumnsGroup> getColumnsList() {
		return oredColumns;
	}

	public String getLoadColumns() {
		return loadColumns;
	}
	
	/**
	 * 获取select，归集joinOn的所有查询
	 * @return
	 */
	public String getSelectSql() {
		String lcs = loadColumns;
		if(joinOns!=null && joinOns.size() > 0) {
			for (JoinOn joinOn : joinOns) {
				String jlc = joinOn.getLoadColumns();
				if(StrUtil.isNotBlank(jlc)) {
					if(StrUtil.isNotBlank(lcs)) {
						lcs += ",";
					}
					lcs += jlc;
				}
			}
		}
		return lcs;
	}

	public String getOrderBy() {
		return orderBy;
	}

	public String getTableName() {
		return tableName;
	}
	
	public List<JoinOn> getJoinOnList() {
		return joinOns;
	}
	
	public Example addColumns(ColumnsGroup columns) {
		// TODO Auto-generated method stub
		oredColumns.add(columns);
		return this;
	}
	
	public Example setColumns(List<ColumnsGroup> oredColumns) {
		this.oredColumns = oredColumns;
		return this;
	}
	
	public Example setLoadColumns(String loadColumns) {
		if(StrUtil.isNotBlank(loadColumns)) {
			loadColumns = loadColumns.trim();
			if(loadColumns.equals("*")) {
				this.loadColumns = loadColumns;
			} else {
				String sls = SqlKit.transactSQLInjection(loadColumns);
				if (SqlKit.validateSQL(sls)) {
					this.loadColumns = sls;
				} else {
					getWarns().add("loadColumns is not validate sql");
				}
			}
		}
		return this;
	}

	public Example setSelectSql(String sql) {
		// TODO Auto-generated method stub
		if(StrUtil.isNotBlank(sql)) {
			sql = sql.trim();
			if(sql.equals("*")) {
				this.loadColumns = sql;
			} else {
				String sls = SqlKit.transactSQLInjection(sql);
				if (SqlKit.validateSQL(sls)) {
					this.loadColumns = sls;
				} else {
					getWarns().add("selectSql is not validate sql");
				}
			}
		}
		return this;
	}

	public Example setOrderBy(String orderBy) {
		// TODO Auto-generated method stub
		if(StrUtil.isNotBlank(orderBy)) {
			orderBy = orderBy.trim();
			String sls = SqlKit.transactSQLInjection(orderBy);
			if (SqlKit.validateSQL(sls)) {
				this.orderBy = sls;
			} else {
				getWarns().add("orderBy is not validate sql");
			}
		}
		return this;
	}

	public Example setTableName(String table) {
		if(StrUtil.isNotBlank(table)) {
			table = table.trim();
			String sls = SqlKit.transactSQLInjection(table);
			if (SqlKit.validateSQL(sls)) {
				this.tableName = sls;
			} else {
				getWarns().add("tableName is not validate sql");
			}
			if(StrUtil.isBlank(alias)) {
				alias = tableName;
			}
		}
		return this;
	}
	
	/**
	 * 
	 */
	public void setRefValue(String refName, LambkitResult result) {
		
	}
	
	public void setRefValue(String refName, Object result) {
		
	}

	/**
	 * 添加para的value到sqlPara中
	 * @param sqlPara
	 */
	public void addValueToParam(Sql sqlPara) {
        if (CollUtil.isNotEmpty(getColumnsList())) {
            for (ColumnsGroup columnsGroup : getColumnsList()) {
            	addValueToParam(sqlPara, columnsGroup);
//            	if (CollUtil.isNotEmpty(columnsGroup.getList())) {
//                    for (Column column : columnsGroup.getList()) {
//                        column.addValueToParam(sqlPara);
//                    }
//                }
//            	for (Columns columns : columnsGroup.getOredColumns()) {
//            		if (CollUtil.isNotEmpty(columns.getList())) {
//                        for (Column column : columns.getList()) {
//                            column.addValueToParam(sqlPara);
//                        }
//                    }
//				}
            }
        }
        if (CollUtil.isNotEmpty(getJoinOnList())) {
        	for (JoinOn jon : getJoinOnList()) {
        		addValueToParam(sqlPara, jon.getColumnsGroup());
        	}
        }
	}
	
	private void addValueToParam(Sql sqlPara, ColumnsGroup columnsGroup) {
		if(columnsGroup==null) {
			return;
		}
		//Printer.print(this, "db"("columnsGroup: " + columnsGroup.getOredColumns().size());
		//Printer.print(this, "db"("columns: " + columnsGroup.getList().size());
        if (CollUtil.isNotEmpty(columnsGroup.getList())) {
            for (Column column : columnsGroup.getList()) {
                column.addValueToParam(sqlPara);
            }
        }
    	for (Columns columns : columnsGroup.getOredColumns()) {
    		if(columns instanceof ColumnsGroup) {
    			ColumnsGroup group = (ColumnsGroup) columns;
    			addValueToParam(sqlPara, group);
    		} else if (CollUtil.isNotEmpty(columns.getList())) {
    			//Printer.print(this, "db"("column: " + columns.getList().size());
                for (Column column : columns.getList()) {
                    column.addValueToParam(sqlPara);
                }
            }
		}
	}

	public GroupBy getGroupBy() {
		return groupBy;
	}

	public Example setGroupBy(GroupBy groupBy) {
		this.groupBy = groupBy;
		return this;
	}

	@Override
	public Example addColumns(Columns columns) {
		// TODO Auto-generated method stub
		columns().columns(columns);
		return this;
	}
	
	public Example add(Columns columns) {
		// TODO Auto-generated method stub
		columns().columns(columns);
		return this;
	}

	public int getPage() {
		return page;
	}

	public void setPage(int page) {
		this.page = page;
	}

	public int getCount() {
		return count;
	}

	public void setCount(int count) {
		this.count = count;
	}
	
	/**
	 * 归集所有消息
	 * @return
	 */
	public List<String> getMessageList() {
		List<String> msg = CollUtil.newArrayList();
		msg.addAll(getWarns());
        if (CollUtil.isNotEmpty(getColumnsList())) {
            for (ColumnsGroup columnsGroup : getColumnsList()) {
            	msg.addAll(columnsGroup.getMessageList());
            }
        }
        if (CollUtil.isNotEmpty(getJoinOnList())) {
        	for (JoinOn jon : getJoinOnList()) {
        		msg.addAll(jon.getMessageList());
        	}
        }
		if(StrUtil.isNotBlank(groupBy.getWarns())) {
			msg.add(groupBy.getWarns());
		}
        return msg;
	}
	
	/**
	 * 归集所有引用
	 * @return
	 */
	public List<String> getRefList() {
		List<String> msg = CollUtil.newArrayList();
        if (CollUtil.isNotEmpty(getColumnsList())) {
            for (ColumnsGroup columnsGroup : getColumnsList()) {
            	msg.addAll(columnsGroup.getRefList());
            }
        }
        if (CollUtil.isNotEmpty(getJoinOnList())) {
        	for (JoinOn jon : getJoinOnList()) {
        		msg.addAll(jon.getRefList());
        	}
        }
        return msg;
	}

	public String getAlias() {
		return alias;
	}

	public void setAlias(String alias) {
		if(alias != null) {
			alias = alias.trim();
			String sls = SqlKit.transactSQLInjection(alias);
			if(SqlKit.validateSQL(sls)) {
				this.alias = sls;
			} else {
				getWarns().add("alias: " + alias + " is not validate sql");
			}
		}
	}

	public String getConfigName() {
		return configName;
	}

	public void setConfigName(String configName) {
		this.configName = configName;
	}
	public List<ColumnsGroup> getOredColumns() {
		return oredColumns;
	}

	public void setOredColumns(List<ColumnsGroup> oredColumns) {
		this.oredColumns = oredColumns;
	}

	public List<JoinOn> getJoinOns() {
		return joinOns;
	}

	public void setJoinOns(List<JoinOn> joinOns) {
		this.joinOns = joinOns;
	}

	public List<String> getWarns() {
		return warns;
	}

	public void setWarns(List<String> warns) {
		this.warns = warns;
	}
}
